package ReadWriteFieldsRelations;

import util.Filter;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.AnnotationNode;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.*;
import java.lang.ClassLoader;
public class MethodRelations implements Opcodes {

  private  List<Class<?>> classes= new LinkedList<Class<?>>();;
  public static final String lineSep = System.getProperty("line.separator");
  protected final Map<Class<?>, Map<Method, ReadWriteFields>> fieldReadWrites = new LinkedHashMap<Class<?>, Map<Method, ReadWriteFields>>();  
  protected final Map<Class<?>, Map<String, ReadWriteFields>> fieldConstructorReadWrites = new LinkedHashMap<Class<?>, Map<String, ReadWriteFields>>();  

  protected final Map<Class<?>, Map<Method, ReadWriteMethods>> dependences = new LinkedHashMap<Class<?>, Map<Method, ReadWriteMethods>>();  
  public static Map  <String,Integer> fieldRead = new HashMap<String,Integer>();
  public static Map <String,Integer>fieldsWrite = new HashMap<String,Integer>();
 
  public MethodRelations(Set<Class<?>> classes) throws ClassNotFoundException {
	    this.classes.addAll(classes);
	}
	  
public Map<Class<?>, Map<Method, ReadWriteFields>> getFieldReadWrites()throws ClassNotFoundException{
	return fieldReadWrites;
}
  public void buildRelations() throws IOException, ClassNotFoundException {
    //analyze each class
    for(Class<?> cls : classes) {
	      ClassReader cr = new ClassReader(cls.getName());
	      ClassNode cn = new ClassNode();
	      cr.accept(cn, ClassReader.SKIP_FRAMES);
	      Map<Method, ReadWriteFields> methodReadWrites = analyzeClass(cls, cn);
	      Map<String, ReadWriteFields> constructorReadWrites = analyzeClassConstructor(cls, cn);
	      this.fieldReadWrites.put(cls, methodReadWrites);
	      this.fieldConstructorReadWrites.put(cls, constructorReadWrites);
    }
   this.computePrivateDependence();
    this.computeDependence();
   }
  public void computePrivateDependence (){
	  boolean flag=false;
	  while(!flag){
		  flag=true;
	    for(Entry<Class<?>, Map<Method, ReadWriteFields>> entry : this.fieldReadWrites.entrySet()) {
		      Map<Method, ReadWriteFields> methodAndReadWrites = entry.getValue();
		      for(Entry<Method, ReadWriteFields> mfields: methodAndReadWrites.entrySet()) {
		    	  String mName=mfields.getKey().toString();
//		    	  System.out.println("MethodRelations.computePrivateDependence  Considering Method: "+ mName);
		    	  ReadWriteFields fields = mfields.getValue();
		    	  Set<Method> methodsCalls= new LinkedHashSet<Method>();
		    	  methodsCalls.addAll(fields.calls());
		    	//  for(Method MethodCall : methodsCalls) {
		    	  while(!methodsCalls.isEmpty()){
		    		Method  MethodCall =methodsCalls.iterator().next();
		    		methodsCalls.remove(MethodCall);
		    		  String mNameCall=MethodCall.toString();
//		    		  System.out.println("  call Method: "+ mNameCall);
		    		  if(mName.equals(mNameCall))
		    			  continue;
		    		  Set<String> readcallMethod= new LinkedHashSet<String>();
		    		  Set<String> writecallMethod=new LinkedHashSet<String>();
		    		  Set<Method> callsMethod=new LinkedHashSet<Method>();
		    		  for(Entry<Class<?>, Map<Method, ReadWriteFields>> entry1 : this.fieldReadWrites.entrySet()) {
		    			  Map<Method, ReadWriteFields> methodAndReadWrites1 = entry1.getValue();
		    		//  Set<String> readcallMethod = methodAndReadWrites.get(MethodCall).readFields.keySet();
		    			  if(methodAndReadWrites1.containsKey(MethodCall)){
		    					  	readcallMethod = methodAndReadWrites1.get(MethodCall).readFields.keySet();
		    					    writecallMethod = methodAndReadWrites1.get(MethodCall).writeFields.keySet();
		    					    callsMethod = methodAndReadWrites1.get(MethodCall).calls;
		    					    break;
		    			  }
		    		  }
		    		  for(String readField : readcallMethod) {
		    			  if(fields.readFieldExist(readField)){

			    			  fields.read(readField);
			    			  flag=false;
		    		      }
		    		  }
		    		
		    		  for(String writeField : writecallMethod) {
		    			  if(fields.writeFieldExist(writeField)){
		    				  fields.write(writeField);
		    				  flag=false;
		    			  }
		    		  }		    		
		    		 
	//	    		  System.out.println("\\  Call Method List: "+ mName);
		    		  for(Method callMethod : callsMethod) {
		    			  if(fields.callMethodExist(callMethod)){
		    				  fields.call(callMethod);
		    				  flag=false;
		    			  }
		    		  }		    		
		    	  }
		      }
	    }
	  }
  }

  String showDependence() {
	    StringBuilder sb = new StringBuilder();
	    
	    for(Entry<Class<?>, Map<Method, ReadWriteMethods>> entry : this.dependences.entrySet()) {
	        sb.append("Class: ");
	        sb.append(entry.getKey());
//	        System.out.println("MethodDependence.showDependence class: "+entry.getKey());
	        sb.append(lineSep);
	        Map<Method, ReadWriteMethods> methodSummary = entry.getValue();
	        for(Entry<Method, ReadWriteMethods> mfields: methodSummary.entrySet()) {
	    		  if(Filter.isPrivate(mfields.getKey()))
	    			  continue;
	    		  if(!Filter.canUse(mfields.getKey()))
	    			  continue;
	       	 sb.append(lineSep);
	          sb.append("  method: ");
	          sb.append(mfields.getKey());       
	          sb.append(lineSep);
	          sb.append("     Read Methods Dependences:");
	          sb.append(lineSep);
	          ReadWriteMethods allMethods = mfields.getValue();
	          Map readMethods=allMethods.getReadMethods();
	          Set set = readMethods.keySet();  
	          Iterator iter = set.iterator();  
	            
	          while (iter.hasNext()) {  
	              String className = (String)iter.next();  
	             List methods = (List) readMethods.get(className);  
	             sb.append("        " + className +"   ");
	             sb.append(methods);
	             sb.append(lineSep);
	          }
	          sb.append(lineSep);
	          sb.append("     Write Methods Dependences:");
	          sb.append(lineSep);
	          Map writeMethods=allMethods.getWriteMethods();
	           set = writeMethods.keySet();  
	           iter = set.iterator();  
	            
	          while (iter.hasNext()) {  
	              String className = (String)iter.next();  
	             List methods = (List) writeMethods.get(className);  
	             sb.append("        " + className +"   ");
	             sb.append(methods);
	             sb.append(lineSep);
	          }
	          sb.append(lineSep);
	          sb.append("     call Methods Dependences:");
	          sb.append(lineSep);
	          Map callMethods=allMethods.callMethods;
	           set = callMethods.keySet();  
	           iter = set.iterator();  
	            
	          while (iter.hasNext()) {  
	              String className = (String)iter.next();  
	             List methods = (List) callMethods.get(className);  
	             sb.append("        " + className +"   ");
	             sb.append(methods);
	             sb.append(lineSep);
	          }
	        }
	        sb.append(lineSep + lineSep);
	        System.out.println(sb);
	        System.out.println("");
	      }

	       return sb.toString();
	  }
  
  

 public  Map<Class<?>, Map<String, List<String>>> buildWriteFiledMethodDependence(){
	  Map<Class<?>, Map<String, List<String>>> writeFieldMethods = new LinkedHashMap<Class<?>, Map<String, List<String>>>();  
	  Map<String, List<String>> fieldMethod= new LinkedHashMap <String, List<String>>();
	
	  for(Entry<Class<?>, Map<Method, ReadWriteFields>> entry : this.fieldReadWrites.entrySet()) {
		  Class<?> cName=entry.getKey();

		  if(!Filter.canUse(cName)) continue;

		  Map<Method, ReadWriteFields> methodSummary = entry.getValue();
		  fieldMethod= new LinkedHashMap <String, List<String>>();
		  for(Entry<Method, ReadWriteFields> mfields: methodSummary.entrySet()) {
			   Method mName=mfields.getKey();
			   if(Filter.isPrivate(mName)) continue;
			   ReadWriteFields fields = mfields.getValue();
		       for(Entry<String, Integer> writes : fields.writeFields.entrySet()) {
		    	   String fieldName=writes.getKey().toString();
		    	    if(fieldMethod.get(fieldName) == null)
		    	    	fieldMethod.put(fieldName, new LinkedList<String>());
		    		List<String> methodList = (List<String>) fieldMethod.get(fieldName);
		    		if(!methodList.contains(mName.getName()))
		    				methodList.add(mName.getName());
		       }
		       
		   }
		  //Calculate constructores if they write fields
		  Map<String, ReadWriteFields> writeFieldConstructores= this.fieldConstructorReadWrites.get(cName);
		  for(Entry<String, ReadWriteFields> mfields: writeFieldConstructores.entrySet()) {
			  String mName=mfields.getKey();
			   ReadWriteFields fields = mfields.getValue();
		       for(Entry<String, Integer> writes : fields.writeFields.entrySet()) {
		    	   String fieldName=writes.getKey().toString();
		    	    if(fieldMethod.get(fieldName) == null)
		    	    	fieldMethod.put(fieldName, new LinkedList<String>());
		    		List<String> methodList = (List<String>) fieldMethod.get(fieldName);
		    		methodList.add("<init>");

		       }       

		  }
		  if(fieldMethod.size()!=0)
			  writeFieldMethods.put(cName, fieldMethod);
	//	   fieldMethod.clear();
	  }
	 // System.out.print("");
	  return writeFieldMethods;
  }

 
  String showFieldReadWrites() {
    StringBuilder sb = new StringBuilder();
    for(Entry<Class<?>, Map<String, ReadWriteFields>> entry : this.fieldConstructorReadWrites.entrySet()) {
        sb.append("Class: ");
        sb.append(entry.getKey());
        sb.append(lineSep);
        Map<String, ReadWriteFields> methodSummary = entry.getValue();
        for(Entry<String, ReadWriteFields> cfields: methodSummary.entrySet()) {
            sb.append("Constructor: ");
            	sb.append(cfields.getKey()); 
                sb.append(lineSep);
                sb.append("Fields:");
                sb.append(lineSep);
                ReadWriteFields fields = cfields.getValue();
                sb.append("    All write:" + lineSep);
                for(Entry<String, Integer> writes : fields.writeFields.entrySet()) {
                  sb.append("       " + writes.getKey()+ ", " + writes.getValue());
                  if(fieldsWrite.containsKey(writes.getKey().toString())){
                 	 String name=writes.getKey().toString();
                 	 int totalnum=(Integer) fieldsWrite.get(name)+writes.getValue();
                 	 fieldsWrite.put(name,totalnum);
                  }
                  else{
                 	 int totalnum=writes.getValue();
                 	 String name=writes.getKey().toString();
                 	 fieldsWrite.put(name,totalnum);           	 
                  }
                  sb.append(lineSep);
                }
        }
        }

   for(Entry<Class<?>, Map<Method, ReadWriteFields>> entry : this.fieldReadWrites.entrySet()) {
     sb.append("Class: ");
     sb.append(entry.getKey());

     sb.append(lineSep);
     Map<Method, ReadWriteFields> methodSummary = entry.getValue();
     for(Entry<Method, ReadWriteFields> mfields: methodSummary.entrySet()) {
    	 sb.append(lineSep);
       sb.append("Method: ");
       sb.append(mfields.getKey());       
       sb.append(lineSep);
       sb.append("Fields:");
       sb.append(lineSep);
       ReadWriteFields fields = mfields.getValue();
       sb.append("    All reads:" + lineSep);
       for(Entry<String, Integer> reads : fields.readFields.entrySet()) {
         sb.append("       " + reads.getKey()+ ", " + reads.getValue());
         if(fieldRead.containsKey(reads.getKey().toString())){
        	 String name=reads.getKey().toString();
        	 int totalnum=(Integer) fieldRead.get(name)+reads.getValue();
        	 fieldRead.put(name,totalnum);
         }
         else{
        	 int totalnum=reads.getValue();
        	 String name=reads.getKey().toString();
        	 fieldRead.put(name,totalnum);
     	 
         }
        	
        sb.append(lineSep);
       }
       sb.append(lineSep);
       sb.append("    All writes:" + lineSep);
       for(Entry<String, Integer> writes : fields.writeFields.entrySet()) {
         sb.append("       " + writes.getKey()+ ", " + writes.getValue());
         if(fieldsWrite.containsKey(writes.getKey().toString())){
        	 String name=writes.getKey().toString();
        	 int totalnum=(Integer) fieldsWrite.get(name)+writes.getValue();
        	 fieldsWrite.put(name,totalnum);
         }
         else{
        	 int totalnum=writes.getValue();
        	 String name=writes.getKey().toString();
        	 fieldsWrite.put(name,totalnum);
     	 
         }
        	 
 //        sb.append(lineSep);
       }
       sb.append(lineSep);
	       sb.append("All methods calls:" + lineSep);
	       sb.append("                "+ fields.calls());
	       sb.append(lineSep);
     }
     sb.append(lineSep + lineSep);
//     System.out.println(sb);
//     System.out.println("");

   }
   sb.append(lineSep + lineSep);
   sb.append("Total read fields");
   sb.append(lineSep);
   for (Map.Entry<String,Integer> entry : fieldRead.entrySet()) {
	   sb.append( entry.getKey()+"="+ entry.getValue());
	   sb.append(lineSep);
	}
   sb.append(lineSep + lineSep);
   sb.append("Total write fields");
   sb.append(lineSep);
   for (Map.Entry<String,Integer> entry : fieldsWrite.entrySet()) {
	   sb.append( entry.getKey()+"="+  entry.getValue());
	   sb.append(lineSep);
	}
    return sb.toString();
    
  }
  String showAllMethodConstructorFieldReadWrites() {
	    StringBuilder sb = new StringBuilder();
	    Map<Class<?>, Map<String, List<String>>> methodConstructorWriteField= buildWriteFiledMethodDependence();
	    
	    return null;
  }
  
  /**
   * All private methods below
   * */
  private Map<Method, ReadWriteFields> analyzeClass(Class<?> cls, ClassNode cn) throws ClassNotFoundException{
    Map<Method, ReadWriteFields> methodReadWrites = new LinkedHashMap<Method,ReadWriteFields>();
    //analyze each method
    List<MethodNode> methodNodes = cn.methods;    
    for (MethodNode methodNode : methodNodes) {
			Method method = this.getMethod(cls, methodNode);
			// Method method= methodNode.
			if (method == null) {
				continue;
			}
		 System.out.println("clzz: "+ cls.getName()+ " Method: "+methodNode.name +" desc: "+methodNode.desc );
		          boolean hasAnnotation=false;
		                      if(methodNode.visibleAnnotations!=null){
		                    	  for (Iterator iter = methodNode.visibleAnnotations.iterator(); iter.hasNext(); ) {
		                    		  org.objectweb.asm.tree.AnnotationNode anno   = (org.objectweb.asm.tree.AnnotationNode) iter.next();
		                              if(anno.desc.equals("Ljava/lang/Deprecated;")){
		                                  hasAnnotation=true;
		                                  break;
		                              	}
		                          }
		                      }
		         if(!hasAnnotation){    
			       	  ReadWriteFields readWrites = this.getReadWriteFields(cls, methodNode);
			       	methodReadWrites.put(method, readWrites);
		         }
//			ReadWriteFields readWrites = this.getReadWriteFields(cls,methodNode);
//			methodReadWrites.put(method, readWrites);


		}
    return methodReadWrites;
    
  }
  private Map<String, ReadWriteFields> analyzeClassConstructor(Class<?> cls, ClassNode cn) throws ClassNotFoundException{
	    Map<String, ReadWriteFields> constructorReadWrites = new LinkedHashMap<String,ReadWriteFields>();
	    List<MethodNode> methodNodes = cn.methods;    
	    for(MethodNode methodNode : methodNodes) {

	    //	if(methodNode.name.equals("<clinit>") ) continue;
	      if(methodNode.name.equals("<init>")){
	          boolean hasAnnotation=false;
	                      if(methodNode.visibleAnnotations!=null){
	                    	  for (Iterator iter = methodNode.visibleAnnotations.iterator(); iter.hasNext(); ) {
	                    		  org.objectweb.asm.tree.AnnotationNode anno   = (org.objectweb.asm.tree.AnnotationNode) iter.next();
	                              if(anno.desc.equals("Ljava/lang/Deprecated;")){
	                                  hasAnnotation=true;
	                                  break;
	                              	}
	                          }
	                      }
	         if(!hasAnnotation){    
	       	  	ReadWriteFields readWrites = this.getReadWriteFields(cls, methodNode);
	       	  	if(constructorReadWrites.containsKey(methodNode.name)){
	       	  		//there are more 1 constructions 
	       	  		ReadWriteFields readWritesAlreadySaved=constructorReadWrites.get(methodNode.name);
	       	  		Set<Method> mCallAlreadySaved=readWritesAlreadySaved.calls();
	       	  		for(Method m: readWrites.calls())
	       	  			if(!mCallAlreadySaved.contains(m))
	       	  					mCallAlreadySaved.add(m);
	       	 
	       	      for(Entry<String, Integer> reads : readWrites.readFields.entrySet()) 
	       	    	  readWritesAlreadySaved.read(reads.getKey().toString());
	
		       	      for(Entry<String, Integer> writes : readWrites.writeFields.entrySet()) 
		       	    	  readWritesAlreadySaved.write(writes.getKey().toString());
	       	  			
	       	  		constructorReadWrites.put(methodNode.name, readWritesAlreadySaved);	       	  		
	       	  	}
	       	  	else
		       	  	constructorReadWrites.put(methodNode.name, readWrites);
	         }
	      }
	    }
	    return constructorReadWrites;
  }
  public   Map<Class<?>,List<String>> getWriteMethods(String thisClzName,String thisMethodName)  {
	  Class<?> thisMethodClzName=null;
	  for(Class<?> clz:classes){
		  if(clz.getName().equals(thisClzName))
			  thisMethodClzName=clz;
	  }
	  if(thisMethodClzName==null){
		  System.err.println("MethodRelations.getWriteMethods thisClzName=null");
		  System.exit(1);
	  }
		  
	  Map<Class<?>,  List<String>> writeFieldMethods = new LinkedHashMap<Class<?>, List<String>>();  

	  for(Entry<Class<?>, Map<Method, ReadWriteMethods>> entry : this.dependences.entrySet()) {
	    	List<String> writeThisMethodList= new LinkedList<String>();
//TODO double check if we need this adding init and class name
		  Class<?> otherMethodclazzName=entry.getKey();
		  
		  if(!Filter.canUse(otherMethodclazzName)) continue;
		  
		  Map<Method, ReadWriteMethods> methodSummary = entry.getValue();
		  for(Entry<Method, ReadWriteMethods> mWrites: methodSummary.entrySet()) {
			   Method otherMethod=mWrites.getKey();
	//		   System.out.println("\t"+otherMethod);
			   if(Filter.isPrivate(otherMethod)) continue;
	    		  if(!Filter.canUse(otherMethod))
	    			  continue;
				   ReadWriteMethods ReadWriteOtherMethodsList = mWrites.getValue();
		       if((otherMethodclazzName.equals(thisMethodClzName) && otherMethod.getName().equals(thisMethodName))) {
		    	   // if the method is a public just get its write List methods
					   Map<String, List<String>> writeMethodsList=ReadWriteOtherMethodsList.getWriteMethods();
				       if(writeMethodsList.size()!=0){
				    	   for(Class<?> otherMethoclassName:this.classes){
				    		   if(!Filter.canUse(otherMethoclassName)) continue;				    		
				    		   if(writeMethodsList.get(otherMethoclassName.getName())!=null){
					         		if(Collections.disjoint(writeThisMethodList, writeMethodsList.get(otherMethoclassName.getName()))){
					         			writeThisMethodList.addAll(writeMethodsList.get(otherMethoclassName.getName()));
					         			writeFieldMethods.put(otherMethoclassName, writeThisMethodList);
					         			writeThisMethodList= new LinkedList<String>();
					         		}
				    		   }
				    	   }
				       }
		       }
		       if((otherMethodclazzName.equals(thisMethodClzName) && otherMethod.getName().equals(thisMethodName))) continue;
//		       //Add all the methods that call the thisMethodName as well. It works when the method is a private 
			   Map<String, List<String>> callOtherMethodsList=ReadWriteOtherMethodsList.getcallMethods();
		       if(callOtherMethodsList.size()==0) continue;
		       List<String> callOtherList=callOtherMethodsList.get(thisMethodClzName.getName());
		       if(callOtherList==null||callOtherList.size()==0) continue;
		       if(!callOtherList.contains(thisMethodName)) continue;
	    		   if(callOtherMethodsList.containsKey(thisMethodClzName.getName())){
	    		       writeThisMethodList=writeFieldMethods.get(otherMethodclazzName);
	    		       if(writeThisMethodList==null){
	    		    	   writeThisMethodList= new LinkedList<String>();
				    	   	writeThisMethodList.add(otherMethod.getName());
						 	writeFieldMethods.put(otherMethodclazzName, writeThisMethodList);  
	    		       }
	    		       else if(!writeThisMethodList.contains(otherMethod.getName())){
				    	   	writeThisMethodList.add(otherMethod.getName());
						 	writeFieldMethods.put(otherMethodclazzName, writeThisMethodList);  
				       }
	    		       //We need to add all the writing methods that write otherMethod (call method) that call thisMethod
					   Map<String, List<String>> writeMethodsList=ReadWriteOtherMethodsList.getWriteMethods();
				       if(writeMethodsList.size()!=0){
				    	   for(Class<?> otherMethoclassName:this.classes){
				    			  if(!Filter.canUse(otherMethoclassName)) continue;				    				
				    		   if(writeMethodsList.get(otherMethoclassName.getName())!=null){
					         		if(Collections.disjoint(writeThisMethodList, writeMethodsList.get(otherMethoclassName.getName()))){
					         			writeThisMethodList.addAll(writeMethodsList.get(otherMethoclassName.getName()));
					         			writeFieldMethods.put(otherMethoclassName, writeThisMethodList);
					         			writeThisMethodList= new LinkedList<String>();
					         		}
				    		   }
				    	   }
				       }
 	    		   }
		  }
			  
	    }
	  return writeFieldMethods;
  }
  public   Map<Class<?>,List<String>> getAllWriteMethods(String targetClass)  {
	  Map<Class<?>,  List<String>> writeFieldMethods = new LinkedHashMap<Class<?>, List<String>>();  
	
	  for(Entry<Class<?>, Map<Method, ReadWriteMethods>> entry : this.dependences.entrySet()) {
		  Class<?> clazz=entry.getKey();
	
		  if(!Filter.canUse(clazz)) continue;

		  if(!clazz.getName().equals(targetClass)) continue;
		  Map<Method, ReadWriteMethods> methodSummary = entry.getValue();
	    	List<String> writeMethodList= new LinkedList<String>();
		  for(Entry<Method, ReadWriteMethods> mWrites: methodSummary.entrySet()) {
			   Method methodz=mWrites.getKey();
			   if(Filter.isPrivate(methodz)) continue;
	    		  if(!Filter.canUse(methodz))
	    			  continue;
		     
			   
			   ReadWriteMethods ReadWriteMethodsList = mWrites.getValue();
			   if(ReadWriteMethodsList.getWriteMethods().get(clazz.getName())!=null)
			   if(ReadWriteMethodsList.getWriteMethods().get(clazz.getName()).contains("<init>"))
		         	if(!writeMethodList.contains("<init>"))
		         			writeMethodList.add("<init>");
			   
			   Map<String, List<String>> writeMethodsList=ReadWriteMethodsList.getReadMethods();
		       if(writeMethodsList.size()==0)continue;
	       
		       if(!writeMethodList.contains(methodz.getName()))
	    	    	writeMethodList.add(methodz.getName());
		  }
		  if(writeMethodList.size()!=0)
			  writeFieldMethods.put(clazz, writeMethodList);
		  	  
	    }
	  return writeFieldMethods;
  }
  public   Map<Class<?>,List<String>> getAllPublicMethods(String targetClass)  {
	  Map<Class<?>,  List<String>> writeFieldMethods = new LinkedHashMap<Class<?>, List<String>>();  
		
	  for(Entry<Class<?>, Map<Method, ReadWriteMethods>> entry : this.dependences.entrySet()) {
		  Class<?> clazz=entry.getKey();
		  if(!clazz.getName().equals(targetClass)) continue;
		  Map<Method, ReadWriteMethods> methodSummary = entry.getValue();
	    	List<String> writeMethodList= new LinkedList<String>();
		  for(Entry<Method, ReadWriteMethods> mWrites: methodSummary.entrySet()) {
			   Method methodz=mWrites.getKey();
			   if(Filter.isPrivate(methodz)) continue;
	    		  if(!Filter.canUse(methodz))
	    			  continue;
		     
		       

	    	    	writeMethodList.add(methodz.getName());
		  }
		  if(writeMethodList.size()!=0)
			  writeFieldMethods.put(clazz, writeMethodList);
		  	  
	    }
	  return writeFieldMethods;

	  
  }
  private ReadWriteFields getReadWriteFields(Class<?> cls, MethodNode methodNode)throws ClassNotFoundException{
	List<String> uitlFields=new LinkedList<String>();
    ReadWriteFields readWrites = new ReadWriteFields();
    InsnList mlist = methodNode.instructions;
    for(int i = 0; i < mlist.size(); i++) {
      AbstractInsnNode insn = mlist.get(i);
      int opCode = insn.getOpcode();
      
      switch(opCode) {
      case ARRAYLENGTH:
    	  if(uitlFields.size()!=0){
 //       	  System.out.println("read array length:"+ uitlFields.get(0));
    		  uitlFields.remove(uitlFields.size()-1);
    	  }
    	  break;
      case LASTORE:
      case FASTORE:
      case DASTORE:
      case IASTORE :
      case CASTORE:
      case AASTORE:
      case BASTORE:
      	  if(uitlFields.size()!=0){
	    	  readWrites.write(uitlFields.get(uitlFields.size()-1));
			  uitlFields.remove(uitlFields.size()-1);
      	  }
    	  
    	  break;
      case INVOKEINTERFACE:

    	  MethodInsnNode  javaUtilMethod = (MethodInsnNode)insn;
    	  
  // 	  System.out.println(javaUtilMethod.getType() + "  "+ javaUtilMethod.getClass().getName() +" " +javaUtilMethod.desc + " " + javaUtilMethod.owner + " "+ javaUtilMethod.name);
    	  if(RequestMethodType.contains(javaUtilMethod.name))
    		  if(uitlFields.size()!=0)
    			  readWrites.write(uitlFields.get(uitlFields.size()-1));

          if(uitlFields.size()!=0)
			  uitlFields.remove(uitlFields.size()-1);
  
    	  readWrites.call(this.getMethod(cls.getName(), javaUtilMethod.name, javaUtilMethod.desc));

    	  break;
      case GETFIELD:
          FieldInsnNode fieldGet = (FieldInsnNode)insn;
//         if(!fieldGet.desc.contains(cls.getName()))
 //       	  if(!uitlFields.contains(fieldGet.name))
        		  uitlFields.add(fieldGet.name);
          readWrites.read(fieldGet.name);
          break;
        case PUTFIELD:
          FieldInsnNode fieldPut = (FieldInsnNode)insn;
          if(uitlFields.size()!=0&&uitlFields.contains(fieldPut.name))
        	  	uitlFields.remove(uitlFields.size()-1);
          readWrites.write(fieldPut.name);
          break;
        case GETSTATIC:
          FieldInsnNode fieldStaticGet = (FieldInsnNode)insn;
         if(fieldStaticGet.owner.startsWith("java")) continue;
          if(!fieldStaticGet.owner.contains(cls.getName()))
        	  uitlFields.add(fieldStaticGet.name);

          readWrites.read(fieldStaticGet.name);
          break;
        case PUTSTATIC:
          FieldInsnNode fieldStaticPut = (FieldInsnNode)insn;
          if(!fieldStaticPut.desc.contains(cls.getName()))
        	  uitlFields.add(fieldStaticPut.name);
          readWrites.write(fieldStaticPut.name);
          break;
        case INVOKEVIRTUAL:
          MethodInsnNode virtualMethod = (MethodInsnNode)insn;
          if(virtualMethod.owner.startsWith("instrumentation")) continue;
          if(virtualMethod.owner.startsWith("java/io/PrintStream")) continue;

          if(!virtualMethod.owner.contains(cls.getName())||virtualMethod.owner.contains("[")){
        	  if(RequestMethodType.contains(virtualMethod.name)){
        		  if(uitlFields.size()!=0){
	        		  readWrites.write(uitlFields.get(uitlFields.size()-1));
	        		  uitlFields.remove(uitlFields.size()-1);
	        		 }       	  
        	  break;
        	  }
         }
//          if(uitlFields.size()!=0)
//			  uitlFields.remove(uitlFields.size()-1);
       
          if(virtualMethod.owner.startsWith("java")) continue;
          readWrites.call(this.getMethod(virtualMethod.owner.replace('/', '.'), virtualMethod.name, virtualMethod.desc));
          break;
        case INVOKESTATIC:
          MethodInsnNode staticMethod = (MethodInsnNode)insn;
          if(staticMethod.owner.startsWith("instrumentation")) continue;
	
          readWrites.call(this.getMethod(staticMethod.owner.replace('/', '.'), staticMethod.name, staticMethod.desc));
          break;         
        case INVOKESPECIAL:
            MethodInsnNode privateMethod = (MethodInsnNode)insn;
//            if(privateMethod.name.equals("<init>"))
//                readWrites.call((privateMethod.owner.replace('/', '.'), privateMethod.name, privateMethod.desc));
//            else           	
            readWrites.call(this.getMethod(privateMethod.owner.replace('/', '.'), privateMethod.name, privateMethod.desc));
            break;   
        }
    }
    
    return readWrites;
  }

  private Method getMethod(Class<?> cls, MethodNode methodNode) {
    return this.getMethod(cls.getName(), methodNode.name, methodNode.desc);
  }
  
  private Method getMethod(String clsOwner, String methodName, String methodDesc)   {
	    for(Class<?> clazz : classes) {
	    	if(!clsOwner.equals(clazz.getName())) continue;
			  Method[] methods = clazz.getDeclaredMethods();
			//	  Method[] methods = cls.getMethods();
			    for(Method method : methods) {
			      if(method.getName().equals(methodName)&& Type.getMethodDescriptor(method).equals(methodDesc)) {
			        return method;
			      }
			    }
		    Class<?> javaClasses= clazz.getSuperclass();
		     methods = javaClasses.getDeclaredMethods();
		     for(Method method : methods) {
		         if(method.getName().equals(methodName)&& Type.getMethodDescriptor(method).equals(methodDesc)) {
		           return method;
		         }
		       }
			    }
    return null;
  }
  private void computeDependence() {
	  
    for(Entry<Class<?>, Map<Method, ReadWriteFields>> entry : this.fieldReadWrites.entrySet()) {
		      Class<?> clazz = entry.getKey();
		      Map<Method, ReadWriteFields> methodAndReadWrites = entry.getValue();
      
      		//the method dependence map
      		Map<Method, ReadWriteMethods> methodMap = new LinkedHashMap<Method, ReadWriteMethods>();
  
        	methodMap = this.computeRelatedMethod(methodAndReadWrites);
      
        	//put the class and method dependence to the map
        	this.dependences.put(clazz, methodMap);
    }
  } 

  private Map<Method, ReadWriteMethods> computeRelatedMethod(Map<Method, ReadWriteFields> methodAndReadWrites) {
  
    Map<Method, ReadWriteMethods> relatedMethodMap = new LinkedHashMap<Method, ReadWriteMethods>();
    
    //then compute the dependence between each method
    for(Method method : methodAndReadWrites.keySet()) {
 //   	System.out.println("\n"+method);
    	ReadWriteMethods readWrites = new ReadWriteMethods();
		  if(!Filter.canUse(method))
			  continue;
      //other methods
      for(Entry<Class<?>, Map<Method, ReadWriteFields>> entry : this.fieldReadWrites.entrySet()) {
    	  Class<?> className=entry.getKey();
    	  Map<Method, ReadWriteFields> methodAndReadWritesClass = entry.getValue();
	      Set<Method> otherMethods = methodAndReadWritesClass.keySet();
	      
	      //the list add to the related method map
	      for(Method otherMethod : otherMethods) {
    		  if(!Filter.canUse(otherMethod))
    			  continue;
 //   		  if(otherMethod.getName().equals(method.getName()))
//    			  continue;
 
    		  //System.out.println("\t other:"+otherMethod);	      

    		  Set<String> readThisMethod = methodAndReadWrites.get(method).readFields.keySet();
	        Set<String> writeThisMethod = methodAndReadWrites.get(method).writeFields.keySet();
	        Set<String> readOtherMethod = methodAndReadWritesClass.get(otherMethod).readFields.keySet();
	        Set<String> writeOtherMethod = methodAndReadWritesClass.get(otherMethod).writeFields.keySet();

	        Set<Method> callthisMethod = methodAndReadWrites.get(method).calls;
	        Set<Method> callOtherMethod = methodAndReadWritesClass.get(otherMethod).calls;

	        
	        //compute the read-read dependence
	        //compute the read-write dependence
	        
	        //it should compute reversely
	        Set<String> readWriteFields = new HashSet<String>();
	        Set<String> readReadFields = new HashSet<String>();
	        Set<Method> methodCalls = new HashSet<Method>();

	        for(String readField : readOtherMethod) {
	          if(writeThisMethod.contains(readField)) {
	            readWriteFields.add(readField);
	            readWrites.read(className.getName(),otherMethod.getName());
	          }
	          if(readThisMethod.contains(readField)) {
	            readReadFields.add(readField);
	          }
	        }
	        for(String writeField : writeOtherMethod) {
            if(readThisMethod.contains(writeField)) {
            	readWriteFields.add(writeField);
    	        if(otherMethod == method) 
	      	         continue;
    	        readWrites.write(className.getName(),otherMethod.getName());
            }
            if(readThisMethod.contains(writeField)) {
            	 readWrites.write(className.getName(),otherMethod.getName());
            }
       }

//	        for(String writeField : writeOtherMethod) {
//	            if(writeThisMethod.contains(writeField)) {
//	              readWriteFields.add(writeField);
//	    	        if(otherMethod == method) 
//   	      	         continue;
//	    	        readWrites.write(className.getName(),otherMethod.getName());
//	            }
//	            if(readThisMethod.contains(writeField)) {
//	            	 readWrites.write(className.getName(),otherMethod.getName());
//	            }
//	        }
	        for(Method callMethod : callOtherMethod) {
		          if(callthisMethod.contains(callMethod)) {
		        	  	methodCalls.add(callMethod);
		            	 readWrites.calls(className.getName(),callMethod.getName());
		          }
//		          if(callthisMethod.contains(callMethod)) {
//		            readReadFields.add(readField);
//		          }
		        }
	        //check if the constructor write the fields
	        Map<String, ReadWriteFields> cWriteField=    this.fieldConstructorReadWrites.get(className);
	        Set<String> writeConstructor = cWriteField.get("<init>").writeFields.keySet();
	        for(String writeField : writeConstructor) {
	            if(writeThisMethod.contains(writeField)) {
	              readWriteFields.add(writeField);
	    	        if(otherMethod == method) 
   	      	         continue;
	    	   //     readWrites.write(className,otherMethod.getName().toString());
	    	        readWrites.write(className.getName(),"<init>");
	            }
	            if(readThisMethod.contains(writeField)) {
	            	 readWrites.write(className.getName(),"<init>");
	            }
	        }

	       
      }
	      
     
      }
      
   //   relatedMethodMap.put(method, dependentMethods);
      relatedMethodMap.put(method, readWrites);
    }
    
    return relatedMethodMap;
  }

}